﻿using BITUI.Audio;
using SharpAudio.Codec;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using TTEngine.Impl.Desktop;


namespace ttbit.Audio
{
    public class BGM : TTEngine.IMusic, IDisposable
    {
        int id;
        static int g_id;
        public BGM(System.IO.Stream stream)
        {
            id = g_id++;
            var head = stream.ReadFourCc();
            var ismp3 = false;

            if (head[0] == 0xff && head[1] == 0xfb)//mp3
                ismp3 = true;
            else if (head[0] == 'I' && head[1] == 'D' && head[2] == '3')
                ismp3 = true;

            if (ismp3)
            {
                data = new AudioData_Mp3();
                data.Load(stream);
                return;
            }
            else if (head == "OggS")
            {
                data = new AudioData_Ogg();
                data.Load(stream);
                return;
            }
            else if (head == "RIFF")
            {
                data = new SharpAudio.Codec.Wave.AudioData_Wav();
                data.Load(stream);
                return;
            }

            throw new Exception("unknown format");
        }

        public BGM(IAudioData data)

        {
            this.data = data;
        }
        public IAudioData data;

        public void Dispose()
        {
            if (this.data != null)
            {
                this.data.Dispose();
                this.data = null;
            }
        }
    }
    public static class SoundSystem
    {
        static SoundSystem()
        {
            EnvAL.OpenTrack(bgmtrack);
            if (bgmthread == null)
            {
                bgmthread = new System.Threading.Thread(bgmthreadfunc);
                bgmthread.IsBackground = true;
                bgmthread.Start();
            }
            EnvAL.OpenTrack(cliptrack);

            if (clipthread == null)
            {
                clipthread = new System.Threading.Thread(clipthreadfunc);
                clipthread.IsBackground = true;
                clipthread.Start();
            }
        }
        public static void PlayClip(AudioClip clip, float vol = 1.0f)
        {
            ClipInPlay play = new ClipInPlay();
            play.buf = clip.alldata;
            play.pos = 0;
            play.vol = vol;
            play.end = false;
            lock (clipinplay)
            {
                clipinplay.Enqueue(play);
            }
        }

        class ClipInPlay
        {
            public float[] buf;
            public int pos;
            public float vol;
            public bool end;
        }
        static Queue<ClipInPlay> clipinplay = new Queue<ClipInPlay>();
        static System.Threading.Thread clipthread;
        const int cliptrack = 1;
        static float[] clipbuf = new float[4096];
        static short[] clipshortbuf = new short[4096];
        static void clipthreadfunc()
        {
            int scount = clipbuf.Length;

            while (true)
            {
                lock (clipinplay)
                {
                    //Array.Fill<float>(clipbuf, 0);
                    //Array.Fill<short>(clipshortsample, 0);
                    for (var i = 0; i < scount; i++)
                    {
                        float sample = 0;
                        foreach (var clip in clipinplay)
                        {
                            if (!clip.end)
                            {
                                if (clip.pos < clip.buf.Length)
                                {
                                    var s = clip.buf[clip.pos] * clip.vol * SoundVolume;
                                    //快速声压公式
                                    sample = sample + s - sample * s;
                                    clip.pos++;
                                }
                                else
                                {
                                    clip.end = true;
                                }
                            }
                        }
                        clipshortbuf[i] = (short)(sample * 65535.0f / 2);
                    }
                    if (clipinplay.TryPeek(out var _r))
                    {
                        if (_r.end)
                            clipinplay.TryDequeue(out _);
                    }
                }
                EnvAL.WriteTrack(cliptrack, clipshortbuf, 0, clipshortbuf.Length);

            }
        }
        //public static void Test()
        //{
        //    Env.Audio.OpenTrack(bgmtrack);
        //    var file = FileLoader.Resource.OpenRead("bgm/3.ogg");
        //    if (curData == null)
        //    {
        //        curData = new AudioData_Ogg();
        //        curData.Load(file);

        //    }
        //    if (bgmthread == null)
        //    {
        //        bgmthread = new System.Threading.Thread(bgmthreadfunc);
        //        bgmthread.IsBackground = true;
        //        bgmthread.Start();
        //    }
        //}
        public static float SoundVolume
        {
            get;
            set;
        } = 1.0f;
        public static float BGMVolume
        {
            get;
            set;
        } = 1.0f;

        public static void PlayBGM(BGM bgm)
        {
            //if (bgm == onReady)
            //    return;
            //if (onReady == null && bgm == onPlay)
            //    return;
            if (onReady != null)
            {

                //onReady.Dispose();
                onReady = null;
            }
            onReady = bgm;
        }
        //public static void PlayBGMFile(string filename)
        //{
        //    if (onReady != null)
        //    {
        //        onReady.Dispose();
        //        onReady = null;
        //    }
        //    var ext = System.IO.Path.GetExtension(filename).ToLower();
        //    if (ext == ".ogg")
        //    {
        //        var data = new AudioData_Ogg();
        //        data.Load(filename);
        //        onReady = new BGM(data);
        //    }
        //    else if (ext == ".mp3")
        //    {
        //        var data = new AudioData_Mp3();
        //        data.Load(filename);
        //        onReady = new BGM(data);
        //    }
        //    else
        //    {
        //        throw new Exception("error data.");
        //    }
        //}
        public static void StopBGM()
        {
            if (onReady != null)
            {
                //onReady.Dispose();
                onReady = null;
            }
            bgmfadeout = true;
            bgmfadeTime = DateTime.Now;
        }
        public static void ResetBGM()
        {
            if (onPlay != null)
            {
                onPlay.data.Reset();// = 0;
            }
        }
        static BGM onPlay;
        static BGM onReady;

        static System.Threading.Thread bgmthread;
        const int bgmtrack = 0;
        static float[] bgmbuf = new float[4096];
        static short[] bgmshortbuf = new short[4096];

        static bool bgmfadeout = false;
        static DateTime bgmfadeTime = DateTime.Now;
        static void bgmthreadfunc()
        {

            while (true)
            {
                if (onPlay == null)//检查是不是有进入的BGM
                {
                    if (onReady != null)
                    {
                        onPlay = onReady;
                        onReady = null;
                        continue;
                    }
                }
                if (onPlay != null)
                {
                    float vol = 1.0f;
                    if (onReady != null && bgmfadeout == false)
                    {
                        bgmfadeout = true;
                        bgmfadeTime = DateTime.Now;
                        continue;
                    }
                    if (bgmfadeout)
                    {
                        var time = (float)(DateTime.Now - bgmfadeTime).TotalSeconds;
                        if (time > 1.0f)
                        {
                            bgmfadeout = false;
                            //onPlay.Dispose();
                            onPlay = onReady;
                            onPlay?.data.Reset();
                            onReady = null;
                            continue;
                        }
                        else
                        {
                            vol = (1.0f - time);
                            PlayBufFade(vol * BGMVolume);
                        }
                    }
                    else
                    {
                        PlayBuf(vol * BGMVolume);
                    }
                }
            }


        }
        static void PlayBufFade(float vol)
        {
            float fadespeed = 1.0f / 44100;
            //* 2 * 2.0f 
            int readlen = AudioDataConverter.ReadFloat_44100_2(onPlay.data, bgmbuf, 0, bgmbuf.Length);
            for (var i = 0; i < readlen; i++)
            {
                bgmshortbuf[i] = (short)(bgmbuf[i] * vol * 65535.0 / 2.0);
                if (i % 2 == 1)
                {
                    vol -= fadespeed;
                    if (vol < 0) vol = 0;
                }
            }
            EnvAL.WriteTrack(bgmtrack, bgmshortbuf, 0, readlen);
        }
        static void PlayBuf(float vol)
        {
            int readlen = AudioDataConverter.ReadFloat_44100_2(onPlay.data, bgmbuf, 0, bgmbuf.Length);
            if (readlen == 0)
            {
                onPlay.data.Reset();// = 0;
                return;
            }
            for (var i = 0; i < readlen; i++)
            {
                bgmshortbuf[i] = (short)(bgmbuf[i] * 0.5 * vol * 65535.0 / 2.0);
            }
            EnvAL.WriteTrack(bgmtrack, bgmshortbuf, 0, readlen);
        }
    }
}
